'''
Created on Jun 9, 2012

@author: Timothy A. Mann
'''

import random
import string

def generate_random_mono_key(plaintextSymbols=string.lowercase, ciphertextSymbols=string.uppercase):
    """
    Generates a random monoalphabetic cipher key. This method assumes that 
    there are at least as many ciphertext symbols as there are plaintext symbols.
    
    @param plaintextSymbols a list containing all of the plaintext symbols
    @param ciphertextSymbols a list containing all of the ciphertext symbols
    """
    if len(plaintextSymbols) > len(ciphertextSymbols):
        raise ValueError('The number of plaintext symbols cannot be greater than the number of ciphertext symbols.')
    csymbols = []
    for c in ciphertextSymbols:
        if(c not in csymbols):
            csymbols.append(c)
    random.shuffle(csymbols)
    key = {}
    ind = 0
    for pc in plaintextSymbols:
        key[pc] = csymbols[ind]
        ind = ind + 1
    return key

def reverse_mono_key(key):
    """
    Reverses a monoalphabetic key so that it can be used for deciphering.
    
    @param key a dictionary with a one-to-one mapping of symbols
    @return the reverse monoalphabetic key
    """
    rkey = {}
    for (k,v) in key.items():
        rkey[v] = k
    return rkey

def mono_cipher(text, key):
    """
    Implements a monoalphabetic cipher.
    
    @param text the text to be encrypted/decrypted.
    @param key a dictionary mapping from one symbol to another
    """
    ctext = ""
    for c in text:
        try:
            xc = key[c]
            ctext = ctext + xc
        except (KeyError):
            ctext = ctext + c
    return ctext

def caesar_cipher(text, shift):
    """
    Implements the Casar shift cipher.
    
    @param text the text to be encrypted/decrypted.
    @param shift the number of alphabet letters to shift each character by
    """
    ctext = ""
    for c in text:
        if(c in string.lowercase):
            ind = ((ord(c) - ord('a')) + shift) % len(string.lowercase)
            xc = chr(ord('a') + ind)
            ctext = ctext + xc
        elif(c in string.uppercase):
            ind = ((ord(c) - ord('A')) + shift) % len(string.uppercase)
            xc = chr(ord('A') + ind)
            ctext = ctext + xc
        else:
            ctext = ctext + c
    return ctext

def vignere_encipher(text, key):
    """
    Implements Vignere enciphering.
    
    @param text the text to encipher.
    @param key a string to be used as a key
    """
    lkey = key.lower()
    keyLength = len(lkey)
    ctext = ""
    ind = 0
    for c in text:
        kind = ind % keyLength
        shift = ord(lkey[kind]) - ord('a')
        xc = caesar_cipher(c, shift)
        ctext = ctext + xc
        ind = ind + 1
    return ctext

def vignere_decipher(text, key):
    """
    Implements Vignere deciphering.
    
    @param text the text to decipher
    @param a string to be used as a key
    """
    lkey = key.lower()
    keyLength = len(lkey)
    ctext = ""
    ind = 0
    for c in text:
        kind = ind % keyLength
        kval = ord(lkey[kind]) - ord('a')
        shift = len(string.lowercase) - kval
        xc = caesar_cipher(c, shift)
        ctext = ctext + xc
        ind = ind + 1
    return ctext